package edu.pitt.dbmi.odie.gapp.gwt.client.navigator;

import java.util.ArrayList;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.data.DataSource;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.fields.DataSourceBooleanField;
import com.smartgwt.client.data.fields.DataSourceTextField;
import com.smartgwt.client.types.Cursor;
import com.smartgwt.client.types.Overflow;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
import com.smartgwt.client.widgets.grid.events.RecordDoubleClickEvent;
import com.smartgwt.client.widgets.grid.events.RecordDoubleClickHandler;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.Layout;
import com.smartgwt.client.widgets.layout.VLayout;

import edu.pitt.dbmi.odie.gapp.gwt.client.dao.ODIE_DaoAnalysis;
import edu.pitt.dbmi.odie.gapp.gwt.client.dao.ODIE_DaoAnnotation;
import edu.pitt.dbmi.odie.gapp.gwt.client.dao.ODIE_DaoDocument;
import edu.pitt.dbmi.odie.gapp.gwt.client.util.ODIE_ClientUtils;

/*
 * ODIE_NavigationPanel uses a cascade of fetch and callback cycles
 * to keep its refresh orderly.  The sequence is as follows
 * 
 * Analysis data fetched (refresh)
 * Document data fetched (refreshAnalysis)
 * Current Document Text fetched (refreshDocument)
 * Annotation data fetched (refreshCurrentDocument)
 * Actual Annotation of Document begins (refreshAnnotations)
 * 
 */

public class ODIE_NavigatorPanel extends HLayout {

	public static ODIE_NavigatorPanel self = null;

	/**
	 * Create a remote service proxy to talk to the server-side ODIE_ObaService
	 * service.
	 */
	private final ODIE_NavigatorSelectionServiceAsync navigatorSelectionService = GWT
			.create(ODIE_NavigatorSelectionService.class);

	private ODIE_DaoAnalysis currentAnalysis = null;

	private final HLayout mainLayout = new HLayout();
	private final VLayout navigationLayout = new VLayout();
	private final VLayout documentViewerGroup = new VLayout();
	private final ListGrid analysisGrid = new ListGrid();
	private final ListGrid documentGrid = new ListGrid();
	private final ListGrid annotationGrid = new ListGrid();
	private final ListGrid clsGrid = new ListGrid();
	private final ODIE_GwtDocumentViewer gwtDocumentViewer = new ODIE_GwtDocumentViewer();

	private String currentDocumentTitle = "Current Document";
	private String currentDocumentContent = "<b>No document loaded.</b>";

	// private ODIE_HTMLFlowDocumentViewer htmlFlowViewer = null;

	public ODIE_NavigatorPanel() {
		self = this;
		buildCachedMainLayout();
		draw();
	}

	private void buildCachedMainLayout() {

		analysisGrid.setWidth100();
		analysisGrid.setMargin(10);

		documentGrid.setWidth100();
		documentGrid.setHeight100();
		documentGrid.setMargin(10);
		documentGrid.setAlternateRecordStyles(true);

		annotationGrid.setWidth100();
		annotationGrid.setHeight100();
		annotationGrid.setMargin(10);
		annotationGrid.setAlternateRecordStyles(true);

		buildNavigationGroup(this.navigationLayout);
		buildDocumentViewerGroup(this.documentViewerGroup);

		this.mainLayout.setIsGroup(true);
		this.mainLayout.setGroupTitle("Analysis");
		this.mainLayout.setWidth100();
		this.mainLayout.setHeight100();
		this.mainLayout.setMembersMargin(10);
		this.mainLayout.setLayoutMargin(10);
		this.mainLayout.addMember(this.navigationLayout);
		this.mainLayout.addMember(this.documentViewerGroup);

		this.addMember(this.mainLayout);
	}

	private VLayout buildNavigationGroup(VLayout layout) {

		// layout.setIsGroup(true);
		// layout.setGroupTitle(null);
		layout.setHeight100();
		layout.setWidth("30%");
		layout.setMembersMargin(5);

		// analysisGrid.setDataSource(buildAnalysisDataSource());
		documentGrid.setDataSource(buildDocumentDataSource());
		annotationGrid.setDataSource(buildAnnotationDataSource());

		documentGrid
				.addRecordDoubleClickHandler(buildDocumentRecordDoubleClickListener());
		annotationGrid
				.addRecordClickHandler(buildAnnotationRecordClickListener());

		// layout.addMember(buildGroup(analysisGrid, "Analysis"));
		layout.addMember(buildGroup(documentGrid, "Document"));
		layout.addMember(buildGroup(annotationGrid, "Annotations"));

		return layout;
	}

	private void populateGridData() {
		if (this.currentAnalysis != null) {
			ODIE_DaoDocument currentDocument = this.currentAnalysis
					.getSelectedDocument();

			// ListGridRecord[] analysisRecords =
			// buildDataForAnalysis(this.currentAnalysis);
			this.mainLayout.setGroupTitle(this.currentAnalysis.getTitle());

			ListGridRecord[] documentRecords = buildDataForDocuments(this.currentAnalysis);
			ListGridRecord[] annotationRecords = buildDataForAnnotations(currentDocument);

			// displayRecordCollection(analysisRecords);
			displayRecordCollection(documentRecords);
			displayRecordCollection(annotationRecords);

			// analysisGrid.setData(analysisRecords);
			// ODIE_ClientUtils.selectBasedOnData(analysisGrid);

			documentGrid.setData(documentRecords);
			ODIE_ClientUtils.selectBasedOnData(documentGrid);

			annotationGrid.setData(annotationRecords);
			// ODIE_ClientUtils.selectBasedOnData(annotationGrid) ;

			populateDocumentViewer();
			// populateDocumentViewerOriginal();

			// analysisGrid.redraw();
			documentGrid.redraw();
			annotationGrid.redraw();
			redraw();
		}
	}

	// private void populateDocumentViewerOriginal() {
	//
	// ODIE_HTMLFlowDocumentViewer viewer =
	// buildAnnotatedDocumentDisplayGroupOriginal();
	// viewer.setNavigatorPanel(this);
	// if (viewer != null) {
	// clearMembers(this.documentViewerGroup);
	// this.documentViewerGroup.addMember(viewer);
	// }
	//
	// if (this.currentAnalysis != null) {
	// ODIE_DaoDocument selectedDocument = this.currentAnalysis
	// .getSelectedDocument();
	// this.currentDocumentTitle = (selectedDocument == null) ?
	// "No Document Selected"
	// : selectedDocument.getTitle();
	// } else {
	// this.currentDocumentTitle = "No Document Selected";
	// }
	//
	// this.documentViewerGroup.setGroupTitle(this.currentDocumentTitle);
	//
	// }

	private void populateDocumentViewer() {
		if (this.currentAnalysis != null) {
			buildAnnotatedDocumentDisplayGroup();
			ODIE_DaoDocument selectedDocument = this.currentAnalysis
					.getSelectedDocument();
			this.currentDocumentTitle = (selectedDocument == null) ? "No Document Selected"
					: selectedDocument.getTitle();
			this.documentViewerGroup.setGroupTitle(this.currentDocumentTitle);
		}
	}

	private void displayRecordCollection(ListGridRecord[] recordCollection) {
		StringBuffer sb = new StringBuffer();
		for (ListGridRecord r : recordCollection) {
			String[] attributeKeys = r.getAttributes();
			for (String attributeKey : attributeKeys) {
				sb.append("[");
				sb.append(attributeKey);
				sb.append(" ==> ");
				sb.append(String.valueOf(r.getAttribute(attributeKey)));
				sb.append("] ");
			}
			sb.append("\n");
		}
		// Log.debug(sb.toString());
	}

	private VLayout buildDocumentViewerGroup(VLayout layout) {
		layout.setOverflow(Overflow.HIDDEN);
		// layout.setHeight100();
		layout.setWidth("70%");
		// layout.setLayoutMargin(15);
		// layout.setTop(15);
		layout.setIsGroup(true);
		layout.setGroupTitle(this.currentDocumentTitle);
		layout.addMember(this.gwtDocumentViewer);
		// clearMembers(layout);
		// this.linkItemViewer = buildAnnotatedDocumentDisplayGroup();
		// layout.addMember(this.linkItemViewer);
		// this.htmlFlowViewer = buildAnnotatedDocumentDisplayGroupOriginal() ;
		// layout.addMember(this.htmlFlowViewer) ;
		return layout;
	}

	@SuppressWarnings("unused")
	private void clearMembers(Layout layout) {
		Canvas[] members = layout.getMembers();
		if (members != null) {
			layout.removeMembers(members);
		}
	}

	private ArrayList<ODIE_NavigatableAnnotation> pullAnnotationsFromGrid() {
		final ArrayList<ODIE_NavigatableAnnotation> annotations = new ArrayList<ODIE_NavigatableAnnotation>();
		if (this.annotationGrid != null
				&& this.annotationGrid.getRecords() != null
				&& this.annotationGrid.getRecords().length > 0) {
			int startRowIndex = 0;
			int endRowIndex = this.annotationGrid.getRecords().length;
			for (int annotationEntryIndex = startRowIndex; annotationEntryIndex <= endRowIndex; annotationEntryIndex++) {
				ListGridRecord annotationRecord = this.annotationGrid
						.getRecord(annotationEntryIndex);
				if (annotationRecord != null) {
					String id = annotationRecord.getAttribute("id");
					String sPos = annotationRecord.getAttribute("sPos");
					String ePos = annotationRecord.getAttribute("ePos");
					String ontologyName = annotationRecord
							.getAttribute("ontologyName");
					String clsName = annotationRecord.getAttribute("clsName");
					String color = annotationRecord.getAttribute("color");
					color = (color != null) ? color : "#CA1F7B";
					ODIE_NavigatableAnnotation annotation = new ODIE_NavigatableAnnotation();
					annotation.setAnnotationId(id);
					annotation.setSPos(sPos);
					annotation.setEPos(ePos);
					annotation.setColor(color);
					annotation.setUri("");
					annotation.setRealized(true);
					annotations.add(annotation);
				}
			}
		}
		return annotations;
	}

	private void buildAnnotatedDocumentDisplayGroup() {
		//
		// Here's where we dynamically annotate the document text.
		//
		ArrayList<ODIE_NavigatableAnnotation> annotations = pullAnnotationsFromGrid();
		this.gwtDocumentViewer.annotateAndConvertDocument(this,
				this.currentDocumentContent, annotations);
	}

	// private ODIE_HTMLFlowDocumentViewer
	// buildAnnotatedDocumentDisplayGroupOriginal() {
	// //
	// // Here's where we dynamically annotate the document text.
	// //
	// ArrayList<ODIE_NavigatableAnnotation> annotations =
	// pullAnnotationsFromGrid();
	// this.htmlFlowViewer = new ODIE_HTMLFlowDocumentViewer();
	// this.htmlFlowViewer.convertDocument(this.currentDocumentContent,
	// annotations);
	// return this.htmlFlowViewer;
	// }

	private DataSource buildAnalysisDataSource() {

		DataSource dataSource = new DataSource();
		// dataSource.setDataFormat(DSDataFormat.XML);
		// dataSource.setRecordXPath("//analysis/isSelected[.='true']/..");
		// dataSource.setDataURL("odie_client/snapshot?category=focus");

		DataSourceTextField idField = new DataSourceTextField("id", "id");
		idField.setHidden(true);
		DataSourceTextField titleField = new DataSourceTextField("title",
				"Name");

		dataSource.setFields(idField, titleField);

		ListGridRecord r = new ListGridRecord();
		r.setAttribute("id", "0");
		r.setAttribute("title", "A Test Analyis");
		ListGridRecord[] testRecords = { r };
		dataSource.setTestData(testRecords);

		return dataSource;
	}

	private DataSource buildDocumentDataSource() {

		DataSource dataSource = new DataSource();
		// dataSource.setDataFormat(DSDataFormat.XML);
		// dataSource.setRecordXPath("//document");
		// dataSource.setDataURL("odie_client/snapshot?category=focus");

		DataSourceTextField idField = new DataSourceTextField("id", "id");
		idField.setHidden(true);

		DataSourceTextField odieAnalysisIdField = new DataSourceTextField(
				"odieAnalysisId", "odieAnalysisId");
		odieAnalysisIdField.setHidden(true);

		DataSourceBooleanField isSelectedField = new DataSourceBooleanField(
				"isSelected", "");
		isSelectedField.setHidden(false);
		isSelectedField.setLength(5);

		DataSourceTextField atomIdField = new DataSourceTextField("atomID",
				"atomID");
		atomIdField.setHidden(true);
		DataSourceTextField rawIdField = new DataSourceTextField("rawID",
				"rawID");
		rawIdField.setHidden(true);
		DataSourceTextField titleField = new DataSourceTextField("title",
				"Name");

		dataSource.setFields(idField, odieAnalysisIdField, isSelectedField,
				rawIdField, titleField);

		return dataSource;
	}

	private DataSource buildAnnotationDataSource() {
		DataSource dataSource = new DataSource();
		// dataSource.setDataFormat(DSDataFormat.XML);
		// dataSource.setRecordXPath("//annotation");
		// dataSource.setDataURL("odie_client/snapshot?category=focus");

		DataSourceTextField idField = new DataSourceTextField("id", "id");
		idField.setHidden(true);
		DataSourceTextField odieAnalysisIdField = new DataSourceTextField(
				"odieAnalysisId", "odieAnalysisId");
		odieAnalysisIdField.setHidden(true);
		DataSourceTextField odieDocumentIdField = new DataSourceTextField(
				"odieDocumentId", "odieDocumentId");
		odieDocumentIdField.setHidden(true);

		DataSourceTextField sPosField = new DataSourceTextField("sPos", "sPos");
		sPosField.setLength(6);
		DataSourceTextField ePosField = new DataSourceTextField("ePos", "ePos");
		ePosField.setLength(6);
		DataSourceTextField ontologyNameField = new DataSourceTextField(
				"ontologyName");
		ontologyNameField.setLength(4);
		DataSourceTextField clsNameField = new DataSourceTextField("clsName");
		clsNameField.setLength(45);

		DataSourceTextField colorField = new DataSourceTextField("color");
		colorField.setHidden(true);

		dataSource.setFields(idField, odieAnalysisIdField, odieDocumentIdField,
				sPosField, ePosField, ontologyNameField, clsNameField,
				colorField);

		return dataSource;
	}

	@SuppressWarnings("unused")
	private DataSource buildClsDataSource() {
		DataSource dataSource = new DataSource();
		// dataSource.setDataFormat(DSDataFormat.XML);
		// dataSource.setRecordXPath("cls");
		// dataSource.setDataURL("odie_client/snapshot?category=focus");

		DataSourceTextField idField = new DataSourceTextField("id", "id");
		idField.setHidden(true);
		DataSourceTextField ontologyNameField = new DataSourceTextField(
				"ontologyName");
		DataSourceTextField clsNameField = new DataSourceTextField("clsName");
		DataSourceTextField cuiField = new DataSourceTextField("cui");

		dataSource
				.setFields(idField, ontologyNameField, clsNameField, cuiField);

		return dataSource;
	}

	private ListGridRecord[] buildDataForAnalysis(ODIE_DaoAnalysis daoAnalysis) {
		ListGridRecord[] dataAnalysisSet = new ListGridRecord[] {};
		if (daoAnalysis != null && daoAnalysis.getDocuments() != null
				&& daoAnalysis.getDocuments().length > 0) {
			dataAnalysisSet = new ListGridRecord[1];
			ListGridRecord dataAnalysisRecord = new ListGridRecord();
			dataAnalysisRecord.setAttribute("id", daoAnalysis.getOdieId());
			dataAnalysisRecord.setAttribute("odieId", daoAnalysis.getOdieId());
			dataAnalysisRecord.setAttribute("title", daoAnalysis.getTitle());
			dataAnalysisSet[0] = dataAnalysisRecord;
		}
		return dataAnalysisSet;
	}

	private ListGridRecord[] buildDataForDocuments(ODIE_DaoAnalysis daoAnalysis) {
		ListGridRecord[] dataDocumentSet = new ListGridRecord[] {};
		if (daoAnalysis != null && daoAnalysis.getDocuments() != null
				&& daoAnalysis.getDocuments().length > 0) {
			dataDocumentSet = new ListGridRecord[daoAnalysis.getDocuments().length];
			int documentIdx = 0;
			for (ODIE_DaoDocument daoDocument : daoAnalysis.getDocuments()) {
				ListGridRecord dataDocumentRecord = new ListGridRecord();
				dataDocumentRecord.setAttribute("id", daoDocument.getOdieId());
				dataDocumentRecord.setAttribute("odieAnalysisId", daoDocument
						.getOdieAnalysisId());
				dataDocumentRecord.setAttribute("atomId", daoDocument
						.getAtomId());
				dataDocumentRecord
						.setAttribute("rawId", daoDocument.getRawId());
				dataDocumentRecord.setAttribute("odieId", daoDocument
						.getOdieId());
				dataDocumentRecord
						.setAttribute("title", daoDocument.getTitle());
				dataDocumentRecord.setAttribute("isSelected", daoDocument
						.isSelected());
				dataDocumentSet[documentIdx++] = dataDocumentRecord;
			}
		}
		return dataDocumentSet;
	}

	private ListGridRecord[] buildDataForAnnotations(
			ODIE_DaoDocument daoDocument) {
		ListGridRecord[] dataAnnotationSet = new ListGridRecord[] {};
		int annotationIdx = 0;
		if (daoDocument != null && daoDocument.getAnnotations() != null
				&& daoDocument.getAnnotations().length > 0) {
			dataAnnotationSet = new ListGridRecord[daoDocument.getAnnotations().length];
			for (ODIE_DaoAnnotation daoAnnotation : daoDocument
					.getAnnotations()) {
				ListGridRecord dataAnnotationRecord = new ListGridRecord();
				dataAnnotationRecord.setAttribute("id", daoAnnotation
						.getOdieId());
				dataAnnotationRecord.setAttribute("odieId", daoAnnotation
						.getOdieId());
				dataAnnotationRecord.setAttribute("odieAnalysisId",
						daoAnnotation.getOdieAnalysisId());
				dataAnnotationRecord.setAttribute("odieDocumentId",
						daoAnnotation.getOdieDocumentId());
				dataAnnotationRecord.setAttribute("sPos", daoAnnotation
						.getSPos());
				dataAnnotationRecord.setAttribute("ePos", daoAnnotation
						.getEPos());
				dataAnnotationRecord.setAttribute("ontologyName", daoAnnotation
						.getOntologyName());
				dataAnnotationRecord.setAttribute("clsName", daoAnnotation
						.getClsName());
				dataAnnotationRecord.setAttribute("color", daoAnnotation
						.getCls().getColorAsString());

				dataAnnotationSet[annotationIdx++] = dataAnnotationRecord;

			}
		}
		return dataAnnotationSet;
	}

	private Canvas buildGroup(ListGrid aListGrid, String groupTitle) {
		VLayout group = new VLayout();
		group.setLayoutMargin(10);
		group.setIsGroup(true);
		group.setGroupTitle(groupTitle);
		if (aListGrid != null) {
			group.addChild(aListGrid);
		}

		return group;
	}

	public void refresh() {
		Cursor defaultCursor = getCursor() ;
		setCursor(Cursor.WAIT) ;
		fetchCurrentAnalysis();
		setCursor(defaultCursor) ;
	}
	
	private RecordDoubleClickHandler buildDocumentRecordDoubleClickListener() {
		return new RecordDoubleClickHandler() {
			public void onRecordDoubleClick(RecordDoubleClickEvent event) {
				selectDocument(event.getRecord());
			}
		};
	}

	private RecordClickHandler buildAnnotationRecordClickListener() {
		return new RecordClickHandler() {
			public void onRecordClick(RecordClickEvent event) {
				navigateToAnnotationInViewer(event.getRecord().getAttribute(
						"id"));
			}

		};
	}

	private void fetchCurrentAnalysis() {
		navigatorSelectionService
				.fetchCurrentAnalysis(new AsyncCallback<ODIE_DaoAnalysis>() {
					public void onFailure(Throwable error) {
						;
					}

					public void onSuccess(ODIE_DaoAnalysis result) {
						if (!isLoadedAnalysis(result)) {
							ODIE_NavigatorPanel.this.currentAnalysis = result;
							fetchCurrentDocument();
						}

					}
				});
	}

	private void fetchCurrentDocument() {
		this.currentDocumentContent = null;
		navigatorSelectionService
				.fetchCurrentDocument(new AsyncCallback<String>() {
					public void onFailure(Throwable error) {
						;
					}

					public void onSuccess(String result) {
						ODIE_NavigatorPanel.this.currentDocumentContent = result;
						populateGridData();
					}
				});
	}

	public void positionAnnotation(String extractAnnotationIdFromHTMLFragment) {
		if (this.annotationGrid != null) {
			ListGridRecord[] records = this.annotationGrid.getRecords();
			int selectedIdx = -1;
			if (records != null && records.length > 0) {
				for (int idx = 0; idx < records.length; idx++) {
					String id = records[idx].getAttribute("id");
					if (id != null) {
						if (id.equals(extractAnnotationIdFromHTMLFragment)) {
							selectedIdx = idx;
							break;
						}
					}
				}
				if (selectedIdx >= 0) {
					this.annotationGrid.scrollToRow(selectedIdx);
					this.annotationGrid.selectSingleRecord(selectedIdx);
				}
			}
		}
	}

	private void navigateToAnnotationInViewer(String id) {
		if (this.gwtDocumentViewer != null) {
			this.gwtDocumentViewer.navigateToAnnotation(id);
		}
	}

	private void selectDocument(Record record) {
		this.currentDocumentTitle = record.getAttribute("title");
		String id = record.getAttribute("id");
		String odieAnalysisId = record.getAttribute("odieAnalysisId");
		navigatorSelectionService.selectDocument(odieAnalysisId, id,
				new AsyncCallback<ODIE_DaoAnalysis>() {
					public void onFailure(Throwable error) {
						;
					}

					public void onSuccess(ODIE_DaoAnalysis result) {
						refresh();

					}
				});
	}

	private boolean isLoadedAnalysis(ODIE_DaoAnalysis candidate) {
		boolean result = true;
		if (this.currentAnalysis == null) {
			result = false;
		} else {
			result = result
					&& this.currentAnalysis.getOdieId().equals(
							candidate.getOdieId());
			result = result
					&& this.currentAnalysis
							.getSelectedDocument()
							.getOdieId()
							.equals(candidate.getSelectedDocument().getOdieId());
		}
		// Log.debug("isLoaded returns " + result);
		return result;
	}

	public ODIE_DaoAnalysis getCurrentAnalysis() {
		return currentAnalysis;
	}

	public void setCurrentAnalysis(ODIE_DaoAnalysis currentAnalysis) {
		this.currentAnalysis = currentAnalysis;
	}

}
